# -*- coding: utf-8 -*-
import random
import subprocess
import time
import logging
import argparse
from datetime import datetime
import requests
import base64
from io import BytesIO
import cv2
import numpy as np
from appium import webdriver
# from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
logger = logging

APP = "XHS"

app_package_map = {
    "XHS": ("com.xingin.xhs", ".index.v2.IndexActivityV2")
}


class XHSAppiumOperate:
    def __init__(self, **kwargs):
        self.screen_width = None
        self.screen_height = None
        self.folder_name = kwargs.get("folder_name")
        self.video_nums = kwargs.get("video_nums")
        self.enable_bgm = kwargs.get("enable_bgm") != 0
        self.original = kwargs.get("original") != 0
        self.draft = kwargs.get("draft") != 0
        self.text = self.decode_from_base64(kwargs.get("text"))
        self.content = self.decode_from_base64(kwargs.get("content"))
        self.deviceName = kwargs.get("deviceName")
        self.appium_url = kwargs.get("appium_url")
        self.platformVersion = kwargs.get("platformVersion")
        self.systemPort = kwargs.get("systemPort")
        self.adbPort = kwargs.get("adbPort")
        self.job_id = kwargs.get("job_id")


        self.driver = None
        self.max_retries = 3
        self.failure_count = 0
        self.view_count = 0
        self.like_count = 0
        self.comment_count = 0
        self.follow_count = 0

    def run(self):
        while self.max_retries > 0:
            try:
                if self.driver:
                    self.driver.quit()
                self.connection()
                logger.info(f"设备 {self.deviceName} 连接成功")
                time.sleep(3)

                logger.info("开始执行小红书发图文任务...")
                self.script()

                break
            except Exception as e:
                logger.error(f"发生异常: {e}")
                self.failure_count += 1
                self.max_retries -= 1
                if self.max_retries == 0:
                    self.normal_end()
                    raise Exception("任务失败")
        logger.info("任务结束")

    def upload_screenshot(self, fileName):
        """
        将当前屏幕截图上传至指定接口
        :param job_id: rpaTaskid，即任务ID
        """
        try:
            # 1. 获取截图的 Base64 字符串
            screenshot_base64 = self.driver.get_screenshot_as_base64()

            if not screenshot_base64:
                logger.error("截图失败，无法获取 Base64 数据")
                return False

            # 2. 构造文件数据（模拟 multipart/form-data）
            # 使用指定的文件名格式
            files = {
                'files': [
                    {
                        "file": screenshot_base64,
                        "fileName": f"{fileName}"
                    }
                ]
            }

            # 3. 发送 POST 请求
            url = f"http://172.25.1.30:8889/social/device/task/upload/{self.job_id}"

            response = requests.post(url, json=files)

            if response.status_code == 200:
                logger.info(f"截图上传成功: {response.json()}")
            else:
                logger.error(f"截图上传失败，状态码: {response.status_code}, 响应: {response.text}")

        except Exception as e:
            logger.error(f"上传截图时发生异常: {e}")

    @staticmethod
    def execute_adb_command(adb_command):
        """
        执行单条 ADB 命令

        :param adb_command: 完整的 ADB 命令字符串
        :return: 命令执行结果（stdout）
        """
        try:
            logger.info(f"执行 ADB 命令: {adb_command}")
            result = subprocess.run(
                adb_command,
                shell=True,
                check=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE
            )
            logger.info(f"命令执行成功: {adb_command}")
            return result.stdout.decode('utf-8')
        except subprocess.CalledProcessError as e:
            logger.error(f"命令执行失败: {adb_command}")
            logger.error(f"错误信息: {e.stderr.decode('utf-8')}")

    def initialize_device_settings(self):
        """
        执行设备初始化所需的 ADB 命令
        """
        logger.info("开始初始化设备设置")

        commands = [
            f"adb -s {self.deviceName} -P {self.adbPort} shell settings put global adb_keep_usb_enabled 1",
            f"adb -s {self.deviceName} -P {self.adbPort} shell settings put global miui_optimization 0",
            f"adb -s {self.deviceName} -P {self.adbPort} shell settings put global tcp_keepalive 1",
            f"adb -s {self.deviceName} -P {self.adbPort} shell dumpsys battery unplug",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm enable io.appium.uiautomator2.server",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm enable io.appium.uiautomator2.server.test",
            f"adb -s {self.deviceName} -P {self.adbPort} shell settings put global background_process_limit 5",
            f"adb -s {self.deviceName} -P {self.adbPort} shell am set-standby-bucket io.appium.uiautomator2.server ACTIVE",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm trim-caches 500M",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm clear io.appium.uiautomator2.server",
            f"adb -s {self.deviceName} -P {self.adbPort} shell pm clear io.appium.uiautomator2.server.test",
            f"adb -s {self.deviceName} -P {self.adbPort} shell input keyevent KEYCODE_CLEAR"
        ]

        for cmd in commands:
            try:
                self.execute_adb_command(cmd)
            except Exception as e:
                logger.error(f"执行命令失败: {cmd}")
                logger.error(f"错误详情: {e}")

    def connection(self):
        self.initialize_device_settings()
        desired_caps = {
            "platformName": "Android",
            "platformVersion": self.platformVersion,
            "deviceName": self.deviceName,
            "udid": self.deviceName,
            "automationName": "UiAutomator2",
            "appPackage": app_package_map[APP][0],
            "appActivity": app_package_map[APP][1],
            "noReset": True,
            "fullReset": False,
            "systemPort": int(self.systemPort),
            "adbPort": int(self.adbPort),
            "appWaitForLaunch": True,
            "noSign": True,
            "autoLaunch": True,
            "newCommandTimeout": 500,
            "skipLogcatCapture": True,
            "disableWindowAnimation": True,
            "disableAndroidWatchers": True,
            "adbExecTimeout": 90000,
            "uiautomator2ServerInstallTimeout": 90000,
            "skipDeviceInitialization": True,
        }

        # self.driver = webdriver.Remote(self.appium_url, options=UiAutomator2Options().load_capabilities(desired_caps))
        self.driver = webdriver.Remote(self.appium_url, desired_caps)
        self.screen_width, self.screen_height = self.get_window_size()

    def script(self):
        """
        主流程：执行小红书视频发布的完整自动化流程
        包括：
            - 打开发布页面
            - 选择相册和视频
            - 设置封面与描述
            - 提交发布
        """

        logger.info("开始执行【发布视频】任务")

        # 步骤 1: 打开“发布”页面（点击首页底部的发布按钮）
        self._open_publish_page()

        # 步骤 2: 点击“从相册选择”，进入系统相册界面
        self._select_album()

        # 步骤 3: 进入“全部”相册，并选择指定文件夹中的视频
        self._choose_videos()

        # 步骤 4: 确认已选视频并点击“下一步”
        self._confirm_selected_videos()

        # 步骤 5: 跳过背景音乐设置（如存在“删除音乐”按钮则点击跳过）
        self._skip_background_music()

        # 步骤 6: 点击“选封面”，选择第一帧作为封面
        self._choose_cover()

        # 步骤 7: 确认封面并继续下一步
        self._confirm_cover()

        # 步骤 8: 填写标题和作品描述内容
        self._fill_title_and_content()

        # 步骤 9: 最终提交视频并等待上传完成
        self._submit_video()

        logger.info("【发布视频】任务完成")
        if self.draft is not True:
            try:
                self._get_invitation_link()
            except Exception as e:
                logger.error(f"获取邀请链接失败: {e}")
            # 返回首页并退出 Appium 会话
        self.normal_end()

        try:
            remove_adb = f"adb -s {self.deviceName} -P {self.adbPort} shell rm -rf /sdcard/Movies/*"
            self.execute_adb_command(remove_adb)
        except Exception as e:
            logger.error(f"清理图片文件夹内的文件失败: {e}")

    def _open_publish_page(self):
        try:
            edit_draft = self.find_element_with_retry(
                by=AppiumBy.ANDROID_UIAUTOMATOR,
                value='new UiSelector().textContains("继续编辑")',
                timeout=5
            )
            # edit_draft.click()
            logger.error(f"已经找到【编辑图文笔记吗？】弹窗，执行关闭操作")
            cancel_button = self.find_element_with_retry(
                by=AppiumBy.XPATH,
                value='(//android.widget.FrameLayout[@resource-id="com.xingin.xhs:id/0_resource_name_obfuscated"])[2]/android.widget.ImageView',
                timeout=5
            )
            cancel_button.click()
        except Exception as e:
            logger.error(f"未找到【继续编辑图文笔记吗？】弹窗: {e}")
        logger.info("正在打开【发布】页面")
        element = self.find_element_with_retry(
            by=AppiumBy.XPATH,
            value='//android.widget.RelativeLayout[@content-desc="发布"]',
            timeout=5
        )
        element.click()
        time.sleep(random.uniform(2, 3))

    def _select_album(self):
        logger.info("点击【从相册选择】")
        element = self.find_element_with_retry(
            by=AppiumBy.ANDROID_UIAUTOMATOR,
            value='new UiSelector().text("从相册选择")',
            timeout=5
        )
        element.click()
        time.sleep(random.uniform(2, 3))

    def _choose_videos(self):
        logger.info("进入【全部】相册")
        element = self.find_element_with_retry(
            by=AppiumBy.ANDROID_UIAUTOMATOR,
            value='new UiSelector().text("全部")',
            timeout=5
        )
        element.click()
        time.sleep(random.uniform(2, 3))

        logger.info(f"进入相册：{self.folder_name}")
        element = self.find_element_with_retry(
            by=AppiumBy.ANDROID_UIAUTOMATOR,
            value=f'new UiSelector().text("{self.folder_name}")',
            timeout=5
        )
        element.click()
        time.sleep(random.uniform(2, 3))

        logger.info(f"准备选择 {self.video_nums} 个视频")
        for i in range(self.video_nums):
            index = i * 2 + 4
            logger.debug(f"正在点击第 {i + 1}/{self.video_nums} 个视频 (index={index})")
            element = self.find_element_with_retry(
                by=AppiumBy.XPATH,
                value=f'(//android.widget.ImageView[@resource-id="com.xingin.xhs:id/0_resource_name_obfuscated"])[{index}]',
                timeout=5
            )
            element.click()
            time.sleep(random.uniform(1, 2))

    def _confirm_selected_videos(self):
        logger.info("确认已选图片并进入下一步")
        element = self.find_element_with_retry(
            by=AppiumBy.XPATH,
            value='//android.widget.TextView[@content-desc="下一步"]',
            timeout=5
        )
        element.click()
        time.sleep(10)

    def _skip_background_music(self):
        # if self.enable_bgm:
        #     logger.warning("已选择启用背景音乐")
        # else:
        #     logger.info("已选择不启用背景音乐")
        #     logger.info("尝试跳过背景音乐选择页")
        #     try:
        #         # 关闭面板可能存在
        #         delete_music_btn = self.find_element_with_retry(
        #             by=AppiumBy.ANDROID_UIAUTOMATOR,
        #             value='new UiSelector().descriptionContains("关闭面板")',
        #             timeout=3
        #         )
        #         delete_music_btn.click()
        #         logger.info("已取消背景音乐")
        #     except Exception as e:
        #         logger.warning("未找到音乐相关控件，可能无需设置音乐")

        logger.info("继续下一步")
        next_btn = self.find_element_with_retry(
            by=AppiumBy.ANDROID_UIAUTOMATOR,
            value='new UiSelector().textContains("下一步")',
            timeout=5
        )
        next_btn.click()
        time.sleep(random.uniform(8, 10))

    def _choose_cover(self):
        try:
            # 尝试cancel申请相册位置信息取消按钮
            cancel_button = self.find_element_with_retry(
                by=AppiumBy.ANDROID_UIAUTOMATOR,
                value='new UiSelector().text("取消")',
                timeout=5
            )
            cancel_button.click()
        except Exception as e:
            logger.info(f"无需申请相册位置信息: {e}")

        logger.info("点击【选封面】")
        element = self.find_element_with_retry(
            by=AppiumBy.ANDROID_UIAUTOMATOR,
            value='new UiSelector().text("选封面")',
            timeout=5
        )
        element.click()
        time.sleep(random.uniform(2, 3))

    def _confirm_cover(self):
        try:
            logger.info("确认封面并继续")
            element = self.find_element_with_retry(
                by=AppiumBy.ANDROID_UIAUTOMATOR,
                value='new UiSelector().text("下一步")',
                timeout=5
            )
            element.click()
            time.sleep(random.uniform(5, 6))
            element = self.find_element_with_retry(
                by=AppiumBy.ANDROID_UIAUTOMATOR,
                value='new UiSelector().text("完成")',
                timeout=5
            )
            element.click()
        except Exception as e:
            logger.info(f"未找到【完成】按钮: {e}")
        time.sleep(10)

    def _fill_title_and_content(self):
        logger.info("开始填写标题和作品描述")

        if self.original:

            logger.info("已选择原创")
            setting_button = self.find_element_with_retry(
                by=AppiumBy.ANDROID_UIAUTOMATOR,
                value='new UiSelector().text("设置")',
                timeout=5
            )
            setting_button.click()
            time.sleep(10)
            try:
                self._find_checkbox_button()
                time.sleep(5)
                logger.info("检测是否需要勾选协议，仅仅第一次")
                try:
                    agree_button = self.find_element_with_retry(AppiumBy.ANDROID_UIAUTOMATOR,
                                                                'new UiSelector().textContains("我已阅读并同意")')

                    agree_button.click()
                    time.sleep(3)

                    button = self.find_element_with_retry(AppiumBy.ANDROID_UIAUTOMATOR,
                                                          'new UiSelector().textContains("声明原创")')
                    button.click()
                    time.sleep(10)

                except Exception as e:
                    logger.warning(f"未检测到【声明原创】按钮")
            except Exception as e:
                logger.warning(f"未找到【原创】按钮:{e}")
            finally:
                logger.info("从设置回退到编辑页")
                self.driver.press_keycode(4)

        logger.debug("点击标题输入框")

        title_input = self.find_element_with_retry(
            by=AppiumBy.ANDROID_UIAUTOMATOR,
            value='new UiSelector().textContains("标题")',
            timeout=5
        )
        title_input.click()
        time.sleep(1)
        title_input.send_keys(self.text)
        logger.info("标题内容已填写")

        logger.debug("点击作品描述输入框")
        content_group = self.find_element_with_retry(
            by=AppiumBy.XPATH,
            value='(//android.widget.ScrollView[@resource-id="com.xingin.xhs:id/0_resource_name_obfuscated"])[2]',
            timeout=5
        )
        content_input = content_group.find_element(AppiumBy.XPATH, '//android.widget.EditText')

        content_input.click()
        time.sleep(1)
        # 替换为
        self.send_keys_with_newline(content_input, self.content)
        logger.info("作品描述内容已填写")

    def base64_to_img(self, base64_string):
        """将 base64 字符串转为 OpenCV 图像"""
        img_data = base64.b64decode(base64_string)
        nparr = np.frombuffer(img_data, np.uint8)
        img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
        return img

    def _find_checkbox_button(self):  # 视频点赞按钮

        # 将模板图片转为 base64
        # 从 base64 转回图片
        template_img = self.base64_to_img(
            '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')
        logger.info(f"使用图片识别【checkbox】按钮")
        x, y = self.find_first_template_from_top(template_img)

        # 点击找到的坐标
        self.tap(x, y)
        logger.info("成功点击【checkbox】")
        time.sleep(3)

    def tap(self, x, y):
        """点击屏幕指定坐标"""
        self.driver.tap([(x, y)], 500)

    def take_screenshot(self) -> np.ndarray:
        """截屏并转为OpenCV格式（内存方式）"""
        # 获取截图的PNG数据
        screenshot_data = self.driver.get_screenshot_as_png()

        # 将字节数据转换为numpy数组
        nparr = np.frombuffer(screenshot_data, np.uint8)

        # 解码为OpenCV图像
        image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)

        if image is None:
            raise ValueError("无法解码截图数据")

        return image

    def upscale_image(self, image: np.ndarray, scale_factor: float = 2.0) -> np.ndarray:
        """放大图像"""
        if scale_factor == 1.0:
            return image

        height, width = image.shape[:2]
        new_dimensions = (int(width * scale_factor), int(height * scale_factor))

        # 使用不同的插值方法
        if scale_factor > 1.0:
            # 放大使用立方插值获得更好的效果
            upscaled_image = cv2.resize(image, new_dimensions, interpolation=cv2.INTER_CUBIC)
        else:
            # 缩小使用区域插值
            upscaled_image = cv2.resize(image, new_dimensions, interpolation=cv2.INTER_AREA)

        logging.info(f"图像尺寸从 {width}x{height} 调整为 {new_dimensions[0]}x{new_dimensions[1]}")
        return upscaled_image

    def find_first_template_from_top(self, template_img: np.ndarray, threshold: float = 0.5) -> tuple:
        """模板匹配定位从上往下的第一个匹配坐标"""
        logging.info("开始模板匹配，寻找从上往下的第一个匹配")

        # 截取屏幕并转换为 OpenCV 格式
        screen_img = self.take_screenshot()
        # upscaled_screen_img = self.upscale_image(screen_img, scale_factor=2.0)
        logging.info("截取屏幕成功")

        # 灰度化处理提升性能
        screen_gray = cv2.cvtColor(screen_img, cv2.COLOR_BGR2GRAY)
        template_gray = cv2.cvtColor(template_img, cv2.COLOR_BGR2GRAY)
        logging.info("灰度化处理完成")

        # 使用 TM_CCOEFF_NORMED 算法进行模板匹配
        res = cv2.matchTemplate(screen_gray, template_gray, cv2.TM_CCOEFF_NORMED)

        # 查找所有高于阈值的匹配点
        locations = np.where(res >= threshold)

        # # 如果没有匹配结果
        # if len(locations[0]) == 0:
        #     logging.warning(f"模板匹配失败，未找到高于阈值 {threshold} 的匹配结果")
        #     raise ValueError("未找到匹配图片")

        # 获取模板尺寸
        h, w = template_gray.shape

        # 收集所有匹配点及其匹配率
        matches_with_scores = []
        for pt in zip(*locations[::-1]):
            # 获取该点的匹配分数
            score = res[pt[1], pt[0]]
            # 计算中心点坐标
            center_x = pt[0] + w // 2
            center_y = pt[1] + h // 2
            matches_with_scores.append((pt[0], pt[1], center_x, center_y, score))

        # 按y坐标排序，确保从上到下的顺序
        matches_with_scores.sort(key=lambda x: x[1])

        # 在图像上标注所有匹配结果
        # annotated_img = screen_img.copy()

        # 为每个匹配结果绘制矩形框和文本标注
        # for i, (x, y, center_x, center_y, score) in enumerate(matches_with_scores):
        #     # 绘制矩形框
        #     top_left = (x, y)
        #     bottom_right = (x + w, y + h)
        #
        #     # 根据匹配率设置颜色（绿色到红色的渐变）
        #     # 匹配率越高颜色越绿，越低越红
        #     green_value = int(255 * score)
        #     red_value = int(255 * (1 - score))
        #     color = (red_value, green_value, 0)  # BGR格式
        #
        #     # 绘制矩形框
        #     cv2.rectangle(annotated_img, top_left, bottom_right, color, 2)
        #
        #     # 添加文本标注（坐标和匹配率）
        #     text = f"{i + 1}:({int(center_x)},{int(center_y)})-{score:.2f}"
        #     cv2.putText(annotated_img, text, (x, y - 10),
        #                 cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1, cv2.LINE_AA)

        # 打印所有匹配结果
        # logging.info(f"找到 {len(matches_with_scores)} 个匹配结果（按Y坐标排序）:")
        # for i, (x, y, center_x, center_y, score) in enumerate(matches_with_scores):
        #     logging.info(f"  匹配 {i + 1}: 原始坐标({x}, {y}), 中心坐标({center_x}, {center_y}), 匹配率: {score:.4f}")

        # 保存标注后的图像到本地
        # try:
        #     import os
        #     import tempfile
        #     # 使用临时目录保存图像
        #     root_dir = os.path.dirname(__file__)
        #     temp_dir = os.path.join(root_dir,'tmp')
        #     timestamp = int(time.time())
        #     annotated_img_path = os.path.join(temp_dir, f"template_match_result_{timestamp}.png")
        #
        #     # 保存图像
        #     cv2.imwrite(annotated_img_path, annotated_img)
        #     logging.info(f"标注后的匹配结果已保存到: {annotated_img_path}")
        # except Exception as e:
        #     logging.error(f"保存标注图像失败: {e}")

        # 选择第一个（最上面的）匹配点
        first_match = matches_with_scores[0]
        x, y, center_x, center_y, score = first_match

        logging.info(f"选择从上往下的第一个匹配结果:")
        logging.info(f"  原始坐标: ({x}, {y})")
        logging.info(f"  中心坐标: ({center_x}, {center_y})")
        logging.info(f"  匹配率: {score:.4f}")

        return center_x, center_y

    def set_clipboard_content(self, text):
        self.driver.set_clipboard_text(text)
        logger.info(f"剪贴板内容已设置为: {text}")

    def send_keys_with_newline(self, element, text):
        # logger.info("正在输入内容：" + text)
        text = text.replace("\\n", "\n")

        if '#' in text:
            # 以第一个 '#' 分割字符串
            parts = text.split('#', 1)
            before_hash = parts[0]
            after_hash = '#' + parts[1]  # 保留 # 和后面的部分

            if "@" in after_hash:
                # 以第一个 '@' 分割字符串
                parts = after_hash.split('@', 1)
                before_at = parts[0]
                after_at = parts[1]  # 保留 @ 和后面的部分
                # element.send_keys("@" + after_at)
                if before_hash:
                    element.send_keys(before_hash)
                    time.sleep(1)
                for i, char in enumerate(before_at):
                    # 设置剪贴板内容为当前字符
                    self.set_clipboard_content(char)
                    time.sleep(2)  # 稍作延迟确保设置成功

                    # 模拟粘贴操作
                    # if i == 0:
                    #     element.click()
                    self.driver.press_keycode(50, 28672)  # 使用粘贴快捷键
                    # KEYCODE PASTE = 50

                    time.sleep(random.uniform(5, 8))  # 稍作延迟确保粘贴成功
                # 空格键的 Android KeyCode 是 62
                self.driver.press_keycode(62)

                # at_base64_img = "iVBORw0KGgoAAAANSUhEUgAAADgAAAA0CAYAAADfRPtlAAAEf0lEQVRoge1ZvXayShTd3723ZLo0+ADwANiHNKnwAcRerTWpUggPIHViT0yVQuiDvZPGCro0WCXNWKXJbcKswYDy57rxLnc1g8Nw9pwz588/X19fX/gf46//WoBj40zw1HEmeOo4Ezx1nAmeOv455uaUUlBKAQCbzQYAIMsyAEDTNGiadszPA8cg6Ps+giAApRSMsb1rCSHQNA26rsMwjKZFAQD8aSoXpZTCcRyEYVjpfVVVMRqNGtdqbYKMMTiOA8/zfvwmyzJ0XQchhJvmZrMBYwxBEHCzFdHpdDAajUAIqSMWRy2CcRzj9vY2pTVJkmCaJgzDQKvVOvi+7/t4fHzEdrvlz1VVxf39fSMkKxOM4xi9Xi91zwzDwHg8Li0YYwzT6RS+7/NnhBAsFovaJCsRZIxhOBymNDeZTNDpdGoJ43kebNvm8yY0WSkO7joT13Vrk8P3/XNdl8/DMITjOLX2LE2QUppyKJPJBKqq1hJChKqqmEwmfO55Ho+lVVCaoHiihmHs1VwSOnq9HtrtNtrtNnq9HhzH4UIzxjAYDHB1dYUgCIBvTYpxcTablRWTo9QdFO+IJEnwPC/zfuwLHSKSAH9zcwN8ZzcPDw98j06nw71r1TteSoPJCQOAaZq55IbD4UFyyX6iU5EkiY8JITBNk8+rmmkpgq+vr3ycl1rtelfDMOC6LlarFVarFVzXTb0rhpnduyyuWy6XZUTlKExQzC1lWc4M4p7n/QgdlmWlBFdVFZZlpRxJHlqtFs+AGGOVtFiKYAJd1zPXPD098XG/3997ZzqdDvr9/sHvit+qkudWioNZdy+O45QA3W734D5F1ojfEtO5oihMMI5jPk7MRoSYOGuaVij7SMqlfRAdjyhDUfz6il6861nVxyEUJljmQ2VMSVyb5USiKOLjKrViJQ1mVeqKovBxGIaFzGn33uatSSCaa1EUJiieXlZMIoTg8vKSz8UAnofdNVkaFGNvlZy3FMHkBOM4ztSQ6PYppbBtO1PbjDHYtp1JSHwmWoIkSZVM9G/Lsqyii9frNd7e3oAcD3hxcZESMooiPD8/4+PjA+/v74iiCL7vw7ZtrNdr/p4kSfj8/OTzJPbd3d3x+359fZ0bf/ehcrK9r+KeTqeYz+eF9kwqksFgwJ+ZpokwDFPaXCwWB1sgWShd0ZumyT2bYRjIM4AgCDCdTnM9rizLsCyLW8FgMMhNxUajUSrxLoPSBCmlqdM+VMaEYYjlcok4jkEI4c5o12EkdaEYFiRJQr/fr0wOVXsylmWlGkSu6zZW1SfdcEVRoChKJbMUUbnptHvaTTSdjoHKbcPdihs1mraMMURRlFuG1UGtxm+WJgkh6Ha7pRq/8/kcjDEQQvDy8lJVnEw00rrfbdomaLVa0HUdkiRxsnEcY7vdglL6I02TJCnVFmkCjf75MpvNKvdOZFnGeDyuFMz3oTGCCZI+ZhAEhaqKy8tL6Lp+NAfVOEERiRlut1ueUyamqmkaFEVp7F+kPByV4G/Ar6/o6+JM8NRxJnjqOBM8dfwLRP2JONpin0oAAAAASUVORK5CYII="
                # screenshot_base64 = self.driver.get_screenshot_as_base64()
                # center_x, center_y = self.find_template_position(screenshot_base64, at_base64_img)
                # if center_x is not None and center_y is not None:
                #     self.driver.tap([(center_x, center_y)])
                # else:
                #     print("未找到@的位置")
                #
                # active_element = self.driver.switch_to.active_element

                # active_element.send_keys(after_at)
                # self.execute_adb_command(f'adb -s {self.deviceName} -P {self.adbPort} shell input text {after_at}')

                self.set_clipboard_content("@" + after_at + "好")
                self.driver.press_keycode(50, 28672)
                self.driver.press_keycode(67)
                # time.sleep(6)  # 稍作延迟确保设置成功
                # self.set_clipboard_content(after_at[1:])
                # self.driver.press_keycode(50, 28672)
                time.sleep(11)
                try:
                    title = self.find_element_with_retry(
                        by=AppiumBy.XPATH,
                        value=f'//android.widget.TextView[@text="{after_at}"]',
                        timeout=5
                    )
                    title.click()
                    logger.info(f"已找到{after_at}标题")
                except Exception as e:
                    logger.info(f"第1次未找到@的标题: {e}")

                    self.driver.press_keycode(67)
                    time.sleep(11)
                    try:
                        title = self.find_element_with_retry(
                            by=AppiumBy.XPATH,
                            value=f'//android.widget.TextView[@text="{after_at}"]',
                            timeout=5
                        )
                        title.click()
                        logger.info(f"已找到{after_at}标题")
                        time.sleep(2)
                    except Exception as e:
                        logger.info(f"尝试第2次未找到@的标题: {e}")

            else:
                # 如果 before_hash 中包含 base64 编码的 emoji，进行解码

                # 发送 # 前的内容
                if before_hash:
                    element.send_keys(before_hash)
                    time.sleep(1)
                    # self.d.send_keys(f"""{before_hash}""")
                    # self.set_clipboard_content(before_hash)
                    # time.sleep(2)  # 稍作延迟确保设置成功
                    # self.driver.press_keycode(50, 28672)  # 使用粘贴快捷键
                    # time.sleep(2)  # 稍作延迟确保粘贴成功
                # 逐个字符复制粘贴 # 及之后的内容
                for i, char in enumerate(after_hash):
                    # 设置剪贴板内容为当前字符
                    self.set_clipboard_content(char)
                    time.sleep(2)  # 稍作延迟确保设置成功

                    # 模拟粘贴操作
                    # if i == 0:
                    #     element.click()
                    self.driver.press_keycode(50, 28672)  # 使用粘贴快捷键
                    # KEYCODE PASTE = 50

                    time.sleep(random.uniform(5, 8))  # 稍作延迟确保粘贴成功


        else:
            # 如果没有 #，直接发送全部内容
            element.send_keys(text)
            self.set_clipboard_content(text)
            # time.sleep(2)  # 稍作延迟确保设置成功
            # self.driver.press_keycode(50, 28672)  # 使用粘贴快捷键
            # time.sleep(2)  # 稍作延迟确保粘贴成功
            # self.d.send_keys(f"""{text}""")

    def _submit_video(self):
        logger.info("准备提交视频内容")
        if self.draft:
            logger.info("已选择保存草稿")
            draft_button = self.find_element_with_retry(
                by=AppiumBy.ANDROID_UIAUTOMATOR,
                value='new UiSelector().textContains("草稿")',
                timeout=5
            )
            draft_button.click()
            draft_button = self.find_element_with_retry(
                by=AppiumBy.ANDROID_UIAUTOMATOR,
                value='new UiSelector().textContains("确定")',
                timeout=5
            )
            draft_button.click()
        else:
            publish_button = self.find_element_with_retry(
                by=AppiumBy.ANDROID_UIAUTOMATOR,
                value='new UiSelector().textContains("发布")',
                timeout=5
            )
            publish_button.click()
            logger.info("视频内容已提交，等待发布结果")
        time.sleep(20)  # 视网络情况可调整

    def _get_invitation_link(self):
        logger.info("获取刚发布内容的邀请链接")
        # 刚发布成功会出现在一个元素
        first_post = self.find_element_with_retry(
            by=AppiumBy.XPATH,
            value='(//android.widget.ImageView[@resource-id="com.xingin.xhs:id/0_resource_name_obfuscated"])[1]',
            timeout=5
        )
        first_post.click()

        time.sleep(20)  # 视网络情况可调整

        self.upload_screenshot(fileName="视频播放页")

        logger.info("进入【视频动态】页面")
        self.handle_video_page()

    def handle_video_page(self):
        logger.info("执行视频动态页操作")
        # 随机滑动 2~10 次
        invitation_button = self.find_element_with_retry(
            by=AppiumBy.ANDROID_UIAUTOMATOR,
            value='new UiSelector().textContains("分享")',
            timeout=5
        )
        invitation_button.click()
        logger.info("滚动寻找分享按钮")
        self.swipe_left_from_button()
        invitation_link = self.find_element_with_retry(
            by=AppiumBy.ANDROID_UIAUTOMATOR,
            value='new UiSelector().text("复制链接")',
            timeout=5
        )
        invitation_link.click()
        # text_link = self.extract_xhs_url(self.driver.get_clipboard_text())
        pasted_content = self.get_clipboard_text()
        text_link = self.extract_xhs_url(pasted_content)
        print("TEXT_LINK>{}<TEXT_LINK".format(text_link))
        logger.info(f"TEXT_LINK>{text_link}<TEXT_LINK")
        logger.info("获取视频笔记链接成功")

    def get_clipboard_text(self):
        # 打开系统文本？粘贴，然后读取文本内容？
        # 方法3: 创建临时输入框并粘贴
        # 寻找页面上可能的输入区域
        try:
            # 尝试寻找页面上的编辑框或输入区域
            input_element = self.find_element_with_retry(AppiumBy.XPATH,
                                                         '//android.widget.EditText[@resource-id="com.xingin.xhs:id/0_resource_name_obfuscated"]')

            if input_element:
                # 使用第一个找到的输入框
                input_element.click()
                time.sleep(1)

                # 模拟粘贴操作 (Ctrl+V)
                self.driver.press_keycode(50, 28672)  # KEYCODE_PASTE with Ctrl
                time.sleep(2)

                input_element = self.find_element_with_retry(AppiumBy.XPATH,
                                                             '//android.widget.EditText[@resource-id="com.xingin.xhs:id/0_resource_name_obfuscated"]')

                # 获取粘贴后的内容
                pasted_content = input_element.text
                # logger.info(f"通过粘贴操作获取到内容: {pasted_content}")

                return pasted_content
        except Exception as e:
            logger.warning(f"通过页面输入框粘贴获取内容失败: {e}")

    def extract_xhs_url(self, url_text):
        """
        从文本中提取小红书链接
        :param url_text: 包含链接的文本
        :return: 提取到的链接或原始链接
        """
        import re

        # 检查是否是正常的URL格式
        if url_text.startswith(('http://', 'https://')):
            return url_text

        # 如果是包含口令信息的文本，尝试提取其中的链接
        # 匹配小红书的各种链接格式:
        # 1. http://xhslink.com/...
        # 2. https://www.xiaohongshu.com/...
        url_patterns = [
            r'http[s]?://xhslink\.com/[^\s]+',
            r'http[s]?://www\.xiaohongshu\.com/[^\s]+'
        ]

        for pattern in url_patterns:
            match = re.search(pattern, url_text)
            if match:
                extracted_url = match.group(0)
                logger.info(f"从文本中提取到链接: {extracted_url}")
                return extracted_url

        # 如果没有找到链接，返回原始文本
        logger.warning(f"未在文本中找到链接，使用原始文本: {url_text}")
        return url_text

    def swipe_left_from_button(self, duration=300):
        # 定位“置顶笔记”按钮
        try:
            element = self.find_element_with_retry(
                by=AppiumBy.ANDROID_UIAUTOMATOR,
                value='new UiSelector().textContains("编辑")',
                timeout=5
            )
        except Exception as e:
            logger.error(f"找不到按钮: {e}")
            return None

        # 获取滑动的起始点和结束点
        location = element.location
        size = element.size
        center_x = location['x'] + size['width'] / 2
        center_y = location['y'] + size['height'] / 2
        start_x = center_x
        start_y = center_y
        end_x = center_x * 0.6  # 向左滑动
        end_y = center_y

        # 执行滑动操作
        self.driver.swipe(start_x, start_y, end_x, end_y, duration)
        logger.info(f"从按钮向左滑动")
        self.driver.swipe(start_x, start_y, end_x, end_y, duration)

    def swipe_to_next(self):
        # 向上滑动
        self.driver.swipe(
            self.screen_width / 2,
            self.screen_height * 0.8,
            self.screen_width / 2,
            self.screen_height * 0.2,
            100
        )

    def find_element_with_retry(self, by, value, timeout=5):
        max_retries = 2
        for attempt in range(max_retries + 1):
            try:
                element = WebDriverWait(self.driver, timeout).until(
                    EC.presence_of_element_located((by, value))
                )
                return element
            except Exception as e:
                if attempt < max_retries:
                    logger.error(f"获取元素失败，重试")
                    time.sleep(1)  # 等待1秒后重试
                else:
                    logger.error(f"获取元素失败，超过3次")
                    logger.error(f"元素名称: {value}")
                    raise NoSuchElementException(f"找不到元素: {value}")

    def decode_from_base64(self, base64_text):
        """
        将base64编码的文本解码为原始文本
        :param base64_text: base64编码的字符串
        :return: 解码后的原始文本
        """
        try:
            # 将base64字符串解码为字节
            decoded_bytes = base64.b64decode(base64_text)
            # 将字节解码为字符串
            decoded_text = decoded_bytes.decode('utf-8')
            return decoded_text
        except Exception as e:
            print(f"解码失败: {e}")
            return None

    def get_window_size(self):
        size = self.driver.get_window_size()
        return size['width'], size['height']

    def normal_end(self):
        logger.info("返回首页并退出")
        self.driver.press_keycode(3)  # HOME 键
        self.driver.quit()


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('-j', '--job_id', dest='job_id', type=str, default=None, required=True, help='target job_id')
    parser.add_argument('-u', '--appium_url', dest='appium_url', type=str, default=None, help='target appium_url')
    parser.add_argument('-d', '--deviceName', dest='deviceName', type=str, default=None, help='target deviceName')
    parser.add_argument('-s', '--systemPort', dest='systemPort', type=str, default=None, help='target systemPort')
    parser.add_argument('-a', '--adbPort', dest='adbPort', type=str, default=None, help='target adbPort')
    parser.add_argument('-p', '--platformName', dest='platformName', type=str, default=None, help='target platformName')
    parser.add_argument('-ver', '--platformVersion', dest='platformVersion', type=str, default=None,
                        help='target platformVersion')
    parser.add_argument('-v', '--variables', dest='variables', type=str, default='{}', help='target variables')
    parser.add_argument('-folder_name', '--folder_name', default="Movies", type=str)
    parser.add_argument('-accountId', '--accountId', dest='accountId', type=str, default="000", help='target accountId')
    parser.add_argument('-video_nums', '--video_nums', type=int, required=True, default=1)
    parser.add_argument('-text', '--text', type=str, required=True)
    parser.add_argument('-content', '--content', type=str, required=True)
    parser.add_argument('-original', '--original', type=int, default=0, help="是否原创")
    parser.add_argument('-enable_bgm', '--enable_bgm', type=int, default=0, help="是否支持BGM")
    parser.add_argument('-draft', '--draft', type=int, default=0, help="是否存草稿")
    parser.add_argument('-file', '--file', type=str, required=False)

    args = parser.parse_args()
    XHSAppiumOperate(**vars(args)).run()
